use Sort::Topological qw(toposort);

my $workflownumber=$ARGV[0];

my %dependency ;
my %dep2;

my @unsortednodes;
my @sortednodes;  #topologically sorted nodes
my %filenames;    # example N1 -> prog1   ||  N2->prog2
my %jobid;        # example N1 -> 1000    ||  N2 ->1001
my $idtracker;    #the job id that will be assigned to the job  
my $numberofnodes; #how many jobs are there?
my %working;       #to store the elements are currently working
my $tempfiletracker=0; #if a job have more than one inputs the files are
my @arr;

my $statusfile= "/mnt/dnm/status";
open(INFO, ">>$statusfile");
print INFO "workflow".$workflownumber. " NOK\n";
close(INFO);

$arr[0]=0;
$finishedfile = "finished".$workflownumber;
$workflowfile= "workflow".$workflownumber;
open(INFO, $workflowfile);
@lines = <INFO>;
my $k=0;
foreach my $line (@lines){
        @tokens = split(/\s/, $line);
        @master = split(/:/, $tokens[0]);
        $arraySize = scalar (@tokens);
        #print "\n".@tokens . "\n" . scalar (@tokens) ." \n";
        my @slave=[];

        for($i=2; $i<$arraySize ; $i++){
                $temp=$i-2;
                $slave[$temp]=$tokens[$i];
        }

        $unsortednodes[$k]=$master[0];
        $k++;

        $dependency{$master[0]} = [@slave];
        if($arraySize>2){
        $dep2{$master[0]} = [@slave];      # example: N1 depends on N2, N3
        }
        else{
        $dep2{$master[0]} = [@arr];
        }
        $filenames{$master[0]} ="/mnt/dnm/program/" . $tokens[1];
}
close(INFO);
sub dependency { @{$dependency{$_[0]} || []}; }
@reversesortednodes = toposort(\&dependency, \@unsortednodes); #it returns reverse topolgical sort
@sortednodes = reverse(@reversesortednodes);
foreach my $job (@sortednodes){
        $working{$job}=0;
}
&mainprog;
sub mainprog {
        $numberofnodes = scalar (@sortednodes);
        $idtracker=0;
        foreach my $job (@sortednodes){
                $jobid{$job}=1000+$workflownumber*150+$idtracker;
                $idtracker++;
        }
        $finished=0;
        $works=0;
        $ready=0;
        while(1){
                foreach my $job (@sortednodes){

                        $finished=&is_finished($job);  #check if the job is already finished or not.
                        $works =$working{$job};      #check if a job is submitted but not finished.


                        if(($finished==0) && ($works==0))  {
                                $ready=&checkifdependent($job); #if the job is not finished, check that the jobs that it depends on are finished.
                        }                                       #if they are the job will be assigned.
                        if($ready==1){
                                &submit_job($job);
                        }
                $works = $working{$job};
                #print "\njob " . $job . "  finished:". $finished. "  working ". $works. "  ready: " . $ready." \n";
                $ready=0;$works=0;$finished=0;
                }
                &control_slurm;                                                               #update 'working' items   and  the'finished' file
                $check =&finish_all;                  #check that all jobs are finished or not.
                if($check){
                        print("\nit is finished\n");
                        open(INFO, ">>$statusfile");
                        print INFO "workflow".$workflownumber. " OK\n";
                        close(INFO);
                        return;
                }

        }
}
sub checkifdependent {
#input  : the job
#output : 1 - ready (All jobs that the input node depends on are finished)
#       : 0  - not ready
        $node = $_[0];
        $check=1;
        $ok=1;
        for ($i=0 ; $i<scalar(@{$dep2{$node}}) ; $i++) {
                if(!($dep2{$node}[$i] eq "0") ){
                        $ok = &is_finished ($dep2{$node}[$i] );
                        if($ok==0){
                                $check=0;
                        }
                }
        }
        return $check;
}
sub is_finished{
#input  : the job that will be checked that it is finished or not
#output : 1 - finished
#       : 0 - not finished
        $item=$_[0];
        $found=0;
        open(INFO, $finishedfile);
        @lines = <INFO>;
        foreach $line(@lines) {
                @dep = split(/\s/, $line);
                if (grep {$_ eq $item} @dep) {
                        $found=1;
                }
        }
        close(INFO);
        return $found;
}

sub submit_job{

        $node=$_[0];
        my $totalargs="";
        $prog = $filenames{$node};
        $temp = scalar(@{$dep2{$node}});
        if($temp  > 1 ){
                $middlefilename="/mnt/dnm/temp".$workflownumber."_".$tempfiletracker;
                open MYFILE, ">" , $middlefilename;
                close(MYFILE);
                open (MYFILE, ">> $middlefilename");
                for ($i=0 ; $i< $temp ; $i++) {
                        $tempstring="";
                        open (FILE, "/mnt/dnm/file_".$dep2{$node}[$i] );

                        while (<FILE>) {
                                $tempstring = $tempstring . $_;
                        }
                print "\n\n------>" . $tempstring;
                print MYFILE $tempstring ;
                $tempstring="";
                close(FILE);
                }
        close(MYFILE);
        $inputstring=" < ".$middlefilename;
        }
        elsif($dep2{$node}[0] eq "0"){
                $inputstring = " ";
        }
        else{
                $inputstring=" < /mnt/dnm/file_".$dep2{$node}[0];
        }
        $outputstring = " > /mnt/dnm/file_".$node;

        print  "srun -N1 --jobid ". $jobid{$node} ." " . $prog. $inputstring. $outputstring;
        print ("\n". $node." is submitted with jobid: " . $jobid{$node}. "\n");
        #system ("srun -N1 --jobid ". $jobid{$node} ." " . $prog." &" );
        #. $totalargs . " >> /mnt/dnm/".$node." "
        system (  "srun -N1 --jobid ". $jobid{$node} ." " . $prog. $inputstring. $outputstring . " &" ) ;
        #print "srun -N1 --jobid ". $jobid{$node} ." " . $prog. $inputstring. $outputstring . " &" ;
        $working{$node}=1;


}

#check if any item is finished
sub control_slurm{
        foreach my $job (@sortednodes){
                if($working{$job}==1){
                        $id=$jobid{$job};
                        #print $id." controlling that it is finished or not \n";
                        $prog = "scontrol show job ". $id . " | ";
                        open FH, $prog or die "Failed";
                        $str="";
                        while(<FH>){
                                $str = $str . $_;
                        }
                        close(FH);

                        @tokens = split(/\s/, $str);
                        foreach my $token (@tokens){
                                 @master = split(/=/, $token);
 
			if($master[0] eq "JobState" && $master[1] eq "COMPLETED"){

                                         $added= &addtothelist ($job);
                                         if($added){
                                                print"----->>". $job."  " . $master[0]. "  :  ". $master[1]. "\n";
                                         }
                                }
                        }
                }
        }
}
sub addtothelist{  #add to the finished list
        $node=$_[0];
        $finished=&is_finished ($node);
        if($finished ){ ##if it is already on he "finished" file do not add it again.
                return 0 ;
        }
        $working{$node}=0;
        open(INFO, ">>$finishedfile");
        print INFO "$node"." ";
        close(INFO);
        return 1;
}


sub finish_all {
##If it could not find one of the nodes in finished file, the workflow is not finished
##return 0 if not finished
#return 1 if finished
        $finished_all=1;
        open(INFO, $finishedfile);
        @lines = <INFO>;
        foreach $node (@sortednodes){
                $found=0;
                foreach $line(@lines) {
                        @dep = split(/\s/, $line);
                        if (grep {$_ eq $node} @dep) {
                                $found=1;
                        }
                 }
                if($found== 0){
                        $finished_all=0;
                }
        }
        close(INFO);
        return $finished_all;
}
